package util
{
	import flash.utils.ByteArray;
	import flash.utils.Dictionary;
	
	import data.LabelInfo;
	import data.Opcode;
	
	import kinds.OpcodeKind;

	public class OpcodeUtil
	{
		public static function code(code:ByteArray):Opcode
		{
			var opcode:Opcode = new Opcode();
			opcode.position = code.position;
			opcode.flag = code.readUnsignedByte();
			opcode.name = OpcodeKind.opNames[opcode.flag];
			switch(opcode.flag)
			{
				case OpcodeKind.OP_debugfile:
				case OpcodeKind.OP_pushstring:
					opcode.opValue.push(BytesUtil.readU30(code));
					break
				case OpcodeKind.OP_pushnamespace:
					opcode.opValue.push(BytesUtil.readU30(code));
					break
				case OpcodeKind.OP_pushint:
					opcode.opValue.push(BytesUtil.readU30(code));
					break
				case OpcodeKind.OP_pushuint:
					opcode.opValue.push(BytesUtil.readU30(code));
					break;
				case OpcodeKind.OP_pushdouble:
					opcode.opValue.push(BytesUtil.readU30(code));
					break;
				case OpcodeKind.OP_getsuper: 
				case OpcodeKind.OP_setsuper: 
				case OpcodeKind.OP_getproperty: 
				case OpcodeKind.OP_initproperty: 
				case OpcodeKind.OP_setproperty: 
				case OpcodeKind.OP_getlex: 
				case OpcodeKind.OP_findpropstrict: 
				case OpcodeKind.OP_findproperty:
				case OpcodeKind.OP_finddef:
				case OpcodeKind.OP_deleteproperty: 
				case OpcodeKind.OP_istype: 
				case OpcodeKind.OP_coerce: 
				case OpcodeKind.OP_astype: 
				case OpcodeKind.OP_getdescendants:
					opcode.opValue.push(BytesUtil.readU30(code));
					break;
				case OpcodeKind.OP_constructprop:
				case OpcodeKind.OP_callproperty:
				case OpcodeKind.OP_callproplex:
				case OpcodeKind.OP_callsuper:
				case OpcodeKind.OP_callsupervoid:
				case OpcodeKind.OP_callpropvoid:
					opcode.opValue.push(BytesUtil.readU30(code));
					opcode.opValue.push(BytesUtil.readU30(code));
					break;
				case OpcodeKind.OP_newfunction: 
					opcode.opValue.push(BytesUtil.readU30(code));
					break;
				
				case OpcodeKind.OP_callstatic:
					opcode.opValue.push(BytesUtil.readU30(code));
					break;
				case OpcodeKind.OP_newclass: 
					opcode.opValue.push(BytesUtil.readU30(code));
					opcode.opValue.push(BytesUtil.readU30(code));
					break;
				case OpcodeKind.OP_lookupswitch:
					var pos:uint = code.position-1;
					var target:uint = pos + BytesUtil.readS24(code);
					var maxindex:uint = BytesUtil.readU32(code);
					opcode.opValue[0] = target;
					opcode.opValue[1] = maxindex;
					for (var i:int=0; i <= maxindex; i++) {
						target = pos + BytesUtil.readS24(code);
						opcode.opValue.push(target);
					}
					break;
				case OpcodeKind.OP_jump:
				case OpcodeKind.OP_iftrue:     case OpcodeKind.OP_iffalse:
				case OpcodeKind.OP_ifeq:       case OpcodeKind.OP_ifne:
				case OpcodeKind.OP_ifge:       case OpcodeKind.OP_ifnge:
				case OpcodeKind.OP_ifgt:       case OpcodeKind.OP_ifngt:
				case OpcodeKind.OP_ifle:       case OpcodeKind.OP_ifnle:
				case OpcodeKind.OP_iflt:       case OpcodeKind.OP_ifnlt:
				case OpcodeKind.OP_ifstricteq: case OpcodeKind.OP_ifstrictne:
					var offset:int = BytesUtil.readS24(code);
					offset += code.position;
					opcode.opValue.push(offset);
					break;
				case OpcodeKind.OP_inclocal:
				case OpcodeKind.OP_declocal:
				case OpcodeKind.OP_inclocal_i:
				case OpcodeKind.OP_declocal_i:
				case OpcodeKind.OP_getlocal:
				case OpcodeKind.OP_kill:
				case OpcodeKind.OP_setlocal:
				case OpcodeKind.OP_debugline:
				case OpcodeKind.OP_getglobalslot:
				case OpcodeKind.OP_getslot:
				case OpcodeKind.OP_setglobalslot:
				case OpcodeKind.OP_setslot:
				case OpcodeKind.OP_pushshort:
				case OpcodeKind.OP_newcatch:
					opcode.opValue.push(BytesUtil.readU30(code));
					break
				case OpcodeKind.OP_debug:
					opcode.opValue.push(code.readUnsignedByte());
					opcode.opValue.push(BytesUtil.readU30(code));
					opcode.opValue.push(code.readUnsignedByte());
					opcode.opValue.push(BytesUtil.readU30(code));
					break;
				case OpcodeKind.OP_newobject:
					opcode.opValue.push(BytesUtil.readU30(code));
					break;
				case OpcodeKind.OP_newarray:
					opcode.opValue.push(BytesUtil.readU30(code));
					break;
				case OpcodeKind.OP_call:
				case OpcodeKind.OP_construct:
				case OpcodeKind.OP_constructsuper:
				case OpcodeKind.OP_applytype:
					opcode.opValue.push(BytesUtil.readU30(code));
					break;
				case OpcodeKind.OP_pushbyte:
				case OpcodeKind.OP_getscopeobject:
					opcode.opValue.push(code.readByte());
					break;
				case OpcodeKind.OP_hasnext2:
					opcode.opValue.push(BytesUtil.readU30(code));
				default:
					/*if (opNames[opcode] == ("0x"+opcode.toString(16).toUpperCase()))
					s += " UNKNOWN OPCODE"*/
					break
			}
			return opcode;
		}
		
		public static function block(code:Array):String
		{
			var labelInfo:LabelInfo = new LabelInfo();
			var str:String = "";
			var len:int = code.length;
			var block:uint = 1;
			var LDic:Dictionary = new Dictionary();
			var i:int = 0;
			var opcode:Opcode = code[i];
			for(i = 0;i < len; i++){
				opcode = code[i];
				switch(opcode.flag){
					case OpcodeKind.OP_jump:
					case OpcodeKind.OP_iftrue:     case OpcodeKind.OP_iffalse:
					case OpcodeKind.OP_ifeq:       case OpcodeKind.OP_ifne:
					case OpcodeKind.OP_ifge:       case OpcodeKind.OP_ifnge:
					case OpcodeKind.OP_ifgt:       case OpcodeKind.OP_ifngt:
					case OpcodeKind.OP_ifle:       case OpcodeKind.OP_ifnle:
					case OpcodeKind.OP_iflt:       case OpcodeKind.OP_ifnlt:
					case OpcodeKind.OP_ifstricteq: case OpcodeKind.OP_ifstrictne:
						var labelName:String = labelInfo.labelFor(opcode.opValue[0]);
						LDic[opcode.position] = labelName + " #" + (opcode.opValue[0]);
						break;
					case OpcodeKind.OP_lookupswitch:
						LDic[opcode.position] = "default: " + labelInfo.labelFor(opcode.opValue[0]);
						var maxindex:int = opcode.opValue[1]; 
						LDic[opcode.position] += " maxcase: " + maxindex
						for (var n:int=0; n <= maxindex; n++) {
							LDic[opcode.position] += " " + labelInfo.labelFor(opcode.opValue[n + 2]);
						}
						break;
					case OpcodeKind.OP_label:
						labelInfo.labelFor(opcode.position);
						break;
				}
			}
			
			for(i = 0;i < len; i++){
				opcode = code[i];
				if(code.flag == OpcodeKind.OP_label || labelInfo.hasOwnProperty(opcode.position)){
					str += "\n" + labelInfo.labelFor(opcode.position) + ": " + "\n";
				}
				
				str += "  ";
				str += opcode.position + ": ";
				str += opcode.toString();
				
				if(LDic.hasOwnProperty(opcode.position)){
					str += LDic[opcode.position] + "\n";
				}
				str += "\n";
			}
			trace(str + "\n\n");
			return str;
		}
	}
}